/**
 * 
 */
package br.org.mestrado;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

import br.org.mestrado.vais.Anticorpo;
import br.org.mestrado.vais.VAIS;
import br.org.mestrado.vais.comparator.ParetoComparator;
import br.org.mestrado.vais.comparator.ParetoMin;
import br.org.mestrado.vais.util.PROBLEMA;

/**
 * @author bertol
 * 
 */
public class Main {

	static final DecimalFormat ddf = new DecimalFormat("0.00",
			new DecimalFormatSymbols(Locale.US));

	// logger
	private static Logger logger = Logger.getLogger(VAIS.class.getPackage()
			.getName());

	public static void main(final String... args) {

		// pega variáveis
		final int NUM_OBJECTIVES = Integer.parseInt(System
				.getProperty("objectives"));
		final int NUM_DECISION_VAR = Integer.parseInt(System
				.getProperty("variables"));
		// controle de qual objetivo deve é minimização
		final String[] control = new String[NUM_OBJECTIVES];
		// critério de parada é o nº de fitness call
		// ou número de gerações
		final int STOP_CRTERIA = Integer.parseInt(System
				.getProperty("stopCriteria"));
		// total de inner loops
		final int INNER_LOOPS = Integer.parseInt(System
				.getProperty("innerLoops"));
		// contador de runs
		final int RUNS = Integer.parseInt(System.getProperty("runs"));
		// contador específico para problemas do japa doidão cosseno
		double START_PROB = Double.parseDouble(System.getProperty("sProb"));
		// amplitude inicial da muta��o
		double START_AMPLITUDE = Double.parseDouble(System.getProperty("sAmp"));
		// contador específico para problemas do japa doidão cosseno
		double END_PROB = Double.parseDouble(System.getProperty("eProb"));
		// população inicial e máxima
		final int N_POP = Integer.parseInt(System.getProperty("nPop"));
		// máximo de Ag na memória
		final int N_MAX_MEN = Integer.parseInt(System.getProperty("nMaxMem"));
		// total de clones por Ag
		final int N_CLONES = Integer.parseInt(System.getProperty("nClones"));
		// problema escolhido
		final PROBLEMA problema = PROBLEMA.valueOf(System
				.getProperty("problema"));

		// incrementador único de ID de clone
		AtomicInteger atomicInteger = new AtomicInteger(N_POP);

		// tipo de avaliação pareto a ser instanciada
		final ParetoMin pareto = new ParetoMin(NUM_OBJECTIVES);

		final ParetoComparator comparator = new ParetoComparator(NUM_OBJECTIVES);

		final String STORE_PATH = System.getProperty("user.home")
				+ "/VIS_many_obj/" + problema.name() + "/" + NUM_OBJECTIVES
				+ "/";

		Thread thrd = Thread.currentThread();

		// logger.getParent().setLevel(Level.FINEST);

		// logger.getParent().getHandlers()[0].setLevel(Level.FINEST);

		// ajusta max fitness
		problema.setMaxFitnessCount(STOP_CRTERIA);

		do {

			final BigDecimal bd_prob = new BigDecimal(START_PROB,
					new MathContext(2, RoundingMode.HALF_EVEN));

			// ajusta comparator
			comparator.setS(bd_prob.doubleValue());

			// inicia classe vais
			final VAIS vais = new VAIS(NUM_OBJECTIVES, NUM_DECISION_VAR,
					STOP_CRTERIA, INNER_LOOPS, RUNS, START_AMPLITUDE, N_POP,
					N_MAX_MEN, N_CLONES, problema, atomicInteger, pareto);

			thrd.setName("Main Thread Name: Total objectives = "
					+ NUM_OBJECTIVES + " | Prob = " + bd_prob.doubleValue());

			logger.info(Thread.currentThread().getName());

			final String prob_n = ddf.format(bd_prob.doubleValue());

			// caminho onde vai ser armazenado os arquivos
			final String path = STORE_PATH + "/" + prob_n;

			// limpa diretórios
			resetOutPut(path);

			Arrays.fill(control, "-");

			// ajusta probabilidade para esta roda
			pareto.setS(bd_prob.doubleValue());

			// contador de runs do problema
			int runsCount = 0;

			do {

				final ArrayList<Anticorpo> pop = new ArrayList<Anticorpo>();

				final ArrayList<Anticorpo> memory = new ArrayList<Anticorpo>();

				logger.info("Start Running: " + runsCount);

				vais.outerLoop(pop, memory);

				logger.info("Run Stop");
				logger.info("Pop size: " + pop.size());

				// incrementa contadores de runs
				runsCount++;

				// escreve saída
				writeOutPut(path, pop);

			} while (runsCount < RUNS);

			START_PROB += 0.05d;
		} while (START_PROB <= END_PROB);

	}

	private static void resetOutPut(final String path) {

		final File dir_base = new File(path);

		if (dir_base.exists()) {

			// zera todos os diretórios
			for (final File dir : dir_base.listFiles()) {
				dir.delete();
			}

			dir_base.delete();

		}

		if (!dir_base.exists())
			dir_base.mkdirs();

	}

	private static void writeOutPut(final String path,
			final ArrayList<Anticorpo> pop) {

		try {

			final File dir = new File(path);

			if (!dir.exists())
				dir.mkdir();

			final PrintWriter writer1 = new PrintWriter(new FileWriter(
					new File(dir, "/run_objective.fit"), true));

			final PrintWriter writer2 = new PrintWriter(new FileWriter(
					new File(dir, "/run_variables.fit"), true));

			for (Anticorpo anticorpo : pop) {

				writer1.println(anticorpo.toString());
				writer2.println(Arrays.toString(anticorpo
						.getDecisionVariables()));

			}
			writer1.println();
			writer2.println();

			writer1.close();
			writer2.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
